using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class ShipProgram  {
	public IShipAPI      Ship;
	public ITextTerminal TextTerminal;
	public IMemory       Memory;
	public GlobalData    Global;
	public IStats        Stats;

	/// <summary>
	/// Use this method to setup star up of your ship
	/// </summary>
	public virtual void Start() {

	}

	/// <summary>
	/// This method is called every server tick
	/// </summary>
	/// <param name="deltaTime"></param>
	//public virtual void Update(float deltaTime) {
	//
	//}

	/// <summary>
	/// Use this method to process user input from console
	/// </summary>
	public virtual void ConsoleInput(string input) {
		ParseInput(input);
	}

	/// <summary>
	/// Use it to save all your data to filesystem.
	/// Called when ship is destroyed, or when server is shutting down.
	/// </summary>
	public virtual void OnProgramShutdown() {

	}

	protected void ParseInput(string input) {
		var args = input.Trim().ToLower().Split(' ');
		if ( args.Length == 0 ) {
			return;
		}
		if ( args[0] == "shoot" ) {
			Ship.ShootGun();
		} else if ( args[0] == "moveto" && args.Length > 3 ) {
			int.TryParse(args[1], out var x);
			int.TryParse(args[2], out var y);
			int.TryParse(args[3], out var z);
			MoveToPoint(new Vector3(x, y, z));
		} else if ( args[0] == "drop" ) {
			DropConsoleCommand(args);
		} else if ( args[0] == "collect" ) {
			Ship.CollectAllCargo();
		} else if ( args[0] == "nearscan" ) {
			NearScanCommand();
		} else if ( args[0] == "farscan" ) {
			FarScanCommand();
		} else if ( args[0] == "inventory" ) {
			InventoryReportCommand();
		} else if ( args[0] == "moverandom" ) {
			var point = GetRandomPoint(50);
			MoveToPoint(point);
			TextTerminal.AddMessage($"Moving to {point.ToString("F0")}");
		} else if ( args[0] == "craft_missile" ) {
			CraftMissileCommand();
		} else if ( args[0] == "launch_missile" ) {
			uint targetId = 0;
			if ( args.Length > 1) {
				uint.TryParse(args[1], out targetId);
			}
			LaunchMissileCommand(targetId);
		} else if ( args[0] == "move_home" ) {
			var home = Stats.BasePosition;
			MoveToPoint(home);
		} else if ( args[0] == "status" ) {
			PrintStatusReportCommand();
		} else if ( args[0] == "help" ) {
			PrintListOfCommands();
		} else if ( args[0] == "look" ) {
			LookAtCommand(args);
		}
		else if ( args[0] == "break" ) {
			BreakCommand();
		}
	}

	protected void PrintListOfCommands() {
		TextTerminal.AddMessage("List of available commands:");
		TextTerminal.AddMessage("status : Print short status report of your ship");
		TextTerminal.AddMessage("move_home : Fly back to your team base");
		TextTerminal.AddMessage("moveto [x] [y] [z] : Fly to set point");
		TextTerminal.AddMessage("look [x] [y] [z] : Look at set point");
		TextTerminal.AddMessage("drop [slot] [count]: Drop items from your ship's cargo compartment");
		TextTerminal.AddMessage("collect : Collect nearby cargo containers");
		TextTerminal.AddMessage("nearscan : Scan nearest surroundings");
		TextTerminal.AddMessage("farscan : Long range scan. Shows only static objects (bases, asteroids)");
		TextTerminal.AddMessage("inventory : Print inventory report");
		TextTerminal.AddMessage("moverandom : move to random point near to your ship");
		TextTerminal.AddMessage("shoot : Shoot Laser gun");	
		TextTerminal.AddMessage("craft_missile : craft guided missile. Requires two Iron blocks and 2000 energy units");
		TextTerminal.AddMessage("launch_missile [target id] : launch guided missile to set target");
		TextTerminal.AddMessage("break : break stuck coroutine without rebooting");

	}

	protected void BreakCommand() {
		Ship.BreakCoroutine();
	}

	protected void LookAtCommand(string[] args) {
		if ( args == null || args.Length < 4) {
			return;
		}
		int.TryParse(args[1], out var x);
		int.TryParse(args[2], out var y);
		int.TryParse(args[3], out var z);
		var target = new Vector3(x, y, z);
		var pos = Ship.ShipPosition;
		var dir = target - pos;
		dir = Vector3.ClampMagnitude(dir, 1f);
		Ship.TargetPoint = pos + dir;
	}

	protected void PrintStatusReportCommand() {
		TextTerminal.AddMessage("Status summary:");
		TextTerminal.AddMessage($"Team: {Stats.Team} HP: {Ship.Health}/100");
		TextTerminal.AddMessage($"Energy: {Ship.Reactor.EnergyLevel.ToString("F0")}/{Ship.Reactor.BatteryCapacity.ToString("F0")}");
		TextTerminal.AddMessage($"Position: {Ship.ShipPosition.ToString("F0")}");
		TextTerminal.AddMessage($"Score: {Stats.Score} Kills: {Stats.KillCount} Deaths: {Stats.DeathCount}");
	}

	protected void CraftMissileCommand() {
		if ( Ship.CraftMissile() ) {
			TextTerminal.AddMessage("Missile crafted");
		} else {
			TextTerminal.AddMessage("Cannot craft missile");
		}
	}

	protected void LaunchMissileCommand(uint targetId) {
		if ( Ship.LaunchMissile(targetId) ) {
			TextTerminal.AddMessage("Missile launched");
		} else {
			TextTerminal.AddMessage("Cannot launch missile");
		}
	}

	protected void InventoryReportCommand() {
		var list = Ship.GetAllInventoryItems();
		var index = 0;
		TextTerminal.AddMessage("Inventory report:");
		foreach ( var item in list ) {
			TextTerminal.AddMessage($"Slot: {index}: Type: {item.Type}, Name: {item.Name}, Count: {item.Count}");
			index++;
		}
		TextTerminal.AddMessage($"Total {Ship.GetInventoryTotalItemsCount()} in {index} slots. Total price: {Ship.GetInventoryTotalPrice()}");
	}

	void DropConsoleCommand(string[] args) {
		if ( args.Length == 1 ) {
			Ship.DropAllCargo();
		} else if ( args.Length == 2 ) {
			int.TryParse(args[1], out var dropSlot);
			Ship.DropFromSlot(dropSlot);
		} else if ( args.Length == 3 ) {
			int.TryParse(args[1], out var dropSlot);
			int.TryParse(args[2], out var count);
			Ship.DropFromSlot(dropSlot, count);
		}
	}

	protected void NearScanCommand() {
		var list = new List<EntitySummary>();
		Ship.Radar.NearScan(100, list);
		TextTerminal.AddMessage($"Near Scan complete. Found {list.Count} entities");
		PrintScanResult(list);
	}

	protected void FarScanCommand() {
		var list = new List<EntitySummary>();
		Ship.Radar.ScanForLargeObjects(500, list);
		TextTerminal.AddMessage($"Far Scan complete. Found {list.Count} entities");
		PrintScanResult(list);
	}

	void PrintScanResult(List<EntitySummary> list) {
		var ourPos = Ship.ShipPosition;
		var index = 1;
		foreach ( var item in list ) {
			TextTerminal.AddMessage($"{index}: Type: {item.Type} ID: {item.ID}, Distance: {Mathf.RoundToInt(Vector3.Distance(ourPos, item.Position))}, Position: {item.Position.ToString("F0")}\nSummary: {item.Summary} ");
			index++;
		}
	}

	protected Vector3 GetRandomPoint(float maxDistance) {
		var point = Ship.ShipPosition + Random.insideUnitSphere * maxDistance;
		return point;
	}

	protected void MoveToPoint(Vector3 point) {
		Ship.AttitudeAutopilotEnabled= true;
		Ship.ThrustAutopilotEnabled= true;
		Ship.TargetPoint= point;
	}

	protected WaitCoroutine StartSubRoutine(IEnumerator subroutine) {
		return new WaitCoroutine(this, subroutine);
	}
}
